package rpc_client

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"git.mycaigou.com/gfyx/common/authorize"
	"git.mycaigou.com/gfyx/common/usermetadata"
	"git.mycaigou.com/ycg/stark-extend/go2skyplugins/go2skygrpc"
	"git.myscrm.cn/golang/stark/v4"
	"git.myscrm.cn/golang/stark/v4/module/client_conn"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"time"
)

const (
	Device = "device"

	TenantCodeKey = "tenant_code"
)

// GetClientConn 获取rpc client对象
func GetClientConn(ctx context.Context, serviceName string) (*grpc.ClientConn, context.Context, error) {
	ctx, err := pairsMetaData(ctx, serviceName)
	if err != nil {
		return nil, ctx, err
	}

	conn, err := client_conn.NewConn(serviceName)
	if err != nil {
		return nil, ctx, err
	}

	conn = conn.WithChainUnaryInterceptor(go2skygrpc.NewUnaryClientInterceptor())

	clientConn, err := conn.GetAPMConn(ctx)
	if err != nil {
		return nil, ctx, err
	}

	return clientConn, ctx, nil
}

type AuthInfo struct {
	AppId     string `json:"app_id"`
	TimeStamp int64  `json:"time_stamp"`
}

// 生成rpc签名字符串
func pairsMetaData(ctx context.Context, serviceName string) (context.Context, error) {
	appId := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(serviceName+".appId", "")
	secret := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(serviceName+".secret", "")
	if appId == "" || secret == "" {
		return ctx, errors.New(fmt.Sprintf("service: %s, appid or serect is null", serviceName))
	}

	var authInfo AuthInfo
	authInfo.AppId = appId
	authInfo.TimeStamp = time.Now().Unix()
	str := fmt.Sprintf("%s&%d&%s", authInfo.AppId, authInfo.TimeStamp, secret)
	sign, err := hmacSha256(str, secret)
	if err != nil {
		return ctx, err
	}
	a, err := json.Marshal(authInfo)
	if err != nil {
		return ctx, err
	}

	md := metadata.Pairs("authorization", "bearer "+fmt.Sprintf("%s.%s", sign, string(a)))

	// 添加租户code信息到上下文
	tenantCode := authorize.GetTenantCode(ctx)
	if tenantCode != "" {
		md.Set(TenantCodeKey, tenantCode)
	}

	//添加ignore_profit到上下文
	ignoreProfit := authorize.GetIgnoreProfit(ctx)
	if ignoreProfit != "" {
		md.Set(authorize.IgnoreProfit, ignoreProfit)
	}

	// 添加用户信息到rpc上下文
	userInfo := usermetadata.GetUserInfo(ctx)
	userInfoBytes, _ := json.Marshal(userInfo)
	md.Set(usermetadata.HEADER_USER_INFO, string(userInfoBytes))

	// 添加设备信息到rpc上下文
	clientIp, ok := ctx.Value(Device).(string)
	if !ok {
		clientIp = ""
	}
	device, ok := ctx.Value(Device).(string)
	if !ok {
		device = "pc"
	}
	deviceInfo := map[string]string{"client_ip": clientIp, "device": device}
	deviceInfoBytes, _ := json.Marshal(deviceInfo)
	md.Set(Device, string(deviceInfoBytes))

	ctx = metadata.NewOutgoingContext(ctx, md)

	// 携带用户信息传递到下游
	ctx, err = usermetadata.SetUserInfoToMeta(ctx)
	if err != nil {
		return ctx, err
	}

	return ctx, nil
}

func hmacSha256(src, key string) (string, error) {
	m := hmac.New(sha256.New, []byte(key))
	_, err := m.Write([]byte(src))
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(m.Sum(nil)), nil
}
